gtktexthandle: Update to gtk4 rendering/input
authorCarlos Garnacho <carlosg@gnome.org>
Tue, 12 Jun 2018 10:33:03 +0000 (12:33 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Thu, 21 Jun 2018 10:54:03 +0000 (12:54 +0200)
GtkTextHandle was neglected by whoever removed the ::draw signal,
leaving it entirely broken. Update to using GtkGizmo so we can
implement snapshot of text handles.

Input has received a revamp too, handling is done through a
GtkGestureDrag and coordinate calculations simplified by storing
the delta to the hotspot on ::begin instead of ::update, as this
value is constant throughout the gesture. Widget state management
on crossing events happens implicitly, so no longer needs to be
done here.

Last but not least, CSS has also been updated so handles are
rendered at the correct size and proportion, and with the padding
that code expects of it.

gtk/gtktexthandle.c
gtk/theme/Adwaita/_common.scss

index ff16630e5d3f69cc218b4a8bb2a84afc5eacbef6..a78691cc78dd4182e68fe2c8c0d99e7827bde7a9 100644 (file)
@@ -23,6 +23,8 @@
 #include "gtkwindowprivate.h"
 #include "gtkcssnodeprivate.h"
 #include "gtkwidgetprivate.h"
+#include "gtkgizmoprivate.h"
+#include "gtkrendericonprivate.h"
 #include "gtkintl.h"
 
 #include <gtk/gtk.h>
@@ -93,30 +95,6 @@ _gtk_text_handle_get_size (GtkTextHandle         *handle,
                          NULL);
 }
 
-static void
-_gtk_text_handle_draw (GtkTextHandle         *handle,
-                       cairo_t               *cr,
-                       GtkTextHandlePosition  pos)
-{
-  GtkTextHandlePrivate *priv;
-  HandleWindow *handle_window;
-  GtkStyleContext *context;
-  gint width, height;
-
-  priv = handle->priv;
-  handle_window = &priv->windows[pos];
-
-  context = gtk_widget_get_style_context (handle_window->widget);
-  _gtk_text_handle_get_size (handle, pos, &width, &height);
-
-  cairo_save (cr);
-  cairo_translate (cr, handle_window->border.left, handle_window->border.top);
-
-  gtk_render_handle (context, cr, 0, 0, width, height);
-
-  cairo_restore (cr);
-}
-
 static gint
 _text_handle_pos_from_widget (GtkTextHandle *handle,
                               GtkWidget     *widget)
@@ -131,28 +109,6 @@ _text_handle_pos_from_widget (GtkTextHandle *handle,
     return -1;
 }
 
-static gboolean
-gtk_text_handle_widget_draw (GtkWidget     *widget,
-                             cairo_t       *cr,
-                             GtkTextHandle *handle)
-{
-  gint pos;
-
-  pos = _text_handle_pos_from_widget (handle, widget);
-
-  if (pos < 0)
-    return FALSE;
-
-#if 0
-  /* Show the invisible border */
-  cairo_set_source_rgba (cr, 1, 0, 0, 0.5);
-  cairo_paint (cr);
-#endif
-
-  _gtk_text_handle_draw (handle, cr, pos);
-  return TRUE;
-}
-
 static void
 gtk_text_handle_set_state (GtkTextHandle *handle,
                            gint           pos,
@@ -181,101 +137,86 @@ gtk_text_handle_unset_state (GtkTextHandle *handle,
   gtk_widget_queue_draw (priv->windows[pos].widget);
 }
 
-static gboolean
-gtk_text_handle_widget_event (GtkWidget     *widget,
-                              GdkEvent      *event,
-                              GtkTextHandle *handle)
+static void
+handle_drag_begin (GtkGestureDrag *gesture,
+                   gdouble         x,
+                   gdouble         y,
+                   GtkTextHandle  *handle)
 {
-  GtkTextHandlePrivate *priv;
-  GdkEventType event_type;
-  gdouble event_x, event_y;
-  guint state;
+  GtkTextHandlePrivate *priv = handle->priv;
+  GtkWidget *widget;
   gint pos;
-  GdkCrossingMode mode;
 
-  priv = handle->priv;
+  widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
   pos = _text_handle_pos_from_widget (handle, widget);
 
-  if (pos < 0)
-    return FALSE;
-
-  event_type = gdk_event_get_event_type (event);
-  gdk_event_get_coords (event, &event_x, &event_y);
-  gdk_event_get_crossing_mode (event, &mode);
-
-  if (event_type == GDK_BUTTON_PRESS)
-    {
-      priv->windows[pos].dx = event_x;
-      priv->windows[pos].dy = event_y;
-      priv->windows[pos].dragged = TRUE;
-      gtk_text_handle_set_state (handle, pos, GTK_STATE_FLAG_ACTIVE);
-      g_signal_emit (handle, signals[DRAG_STARTED], 0, pos);
-    }
-  else if (event_type == GDK_BUTTON_RELEASE)
-    {
-      g_signal_emit (handle, signals[DRAG_FINISHED], 0, pos);
-      priv->windows[pos].dragged = FALSE;
-      gtk_text_handle_unset_state (handle, pos, GTK_STATE_FLAG_ACTIVE);
-    }
-  else if (event_type == GDK_ENTER_NOTIFY)
-    gtk_text_handle_set_state (handle, pos, GTK_STATE_FLAG_PRELIGHT);
-  else if (event_type == GDK_LEAVE_NOTIFY)
-    {
-      if (!priv->windows[pos].dragged &&
-          (mode == GDK_CROSSING_NORMAL ||
-           mode == GDK_CROSSING_UNGRAB))
-        gtk_text_handle_unset_state (handle, pos, GTK_STATE_FLAG_PRELIGHT);
-    }
-  else if (event_type == GDK_MOTION_NOTIFY &&
-           gdk_event_get_state (event, &state) &&
-           state & GDK_BUTTON1_MASK &&
-           priv->windows[pos].dragged)
-    {
-      gint x, y, handle_width, handle_height;
-      cairo_rectangle_int_t rect;
-      GtkAllocation allocation;
-      GtkWidget *window;
-
-      window = gtk_widget_get_parent (priv->windows[pos].widget);
-      gtk_widget_get_allocation (priv->windows[pos].widget, &allocation);
-      _gtk_text_handle_get_size (handle, pos, &handle_width, &handle_height);
-
-      _gtk_window_get_popover_position (GTK_WINDOW (window),
-                                        priv->windows[pos].widget,
-                                        NULL, &rect);
-
-      x = rect.x + event_x - priv->windows[pos].dx;
-      y = rect.y + event_y - priv->windows[pos].dy +
-        priv->windows[pos].border.top / 2;
-
-      if (pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
-          priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
-        x += handle_width / 2;
-      else if ((pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
-                priv->windows[pos].dir == GTK_TEXT_DIR_RTL) ||
-               (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START &&
-                priv->windows[pos].dir != GTK_TEXT_DIR_RTL))
-        x += handle_width;
-
-      gtk_widget_translate_coordinates (window, priv->parent, x, y, &x, &y);
-      g_signal_emit (handle, signals[HANDLE_DRAGGED], 0, pos, x, y);
-    }
+  if (pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
+      priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
+    x -= gtk_widget_get_width (widget) / 2;
+  else if ((pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
+            priv->windows[pos].dir == GTK_TEXT_DIR_RTL) ||
+           (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START &&
+            priv->windows[pos].dir != GTK_TEXT_DIR_RTL))
+    x -= gtk_widget_get_width (widget);
+
+  y += priv->windows[pos].border.top / 2;
+
+  priv->windows[pos].dx = x;
+  priv->windows[pos].dy = y;
+  priv->windows[pos].dragged = TRUE;
+  gtk_text_handle_set_state (handle, pos, GTK_STATE_FLAG_ACTIVE);
+  g_signal_emit (handle, signals[DRAG_STARTED], 0, pos);
+}
 
-  return TRUE;
+static void
+handle_drag_update (GtkGestureDrag *gesture,
+                    gdouble         offset_x,
+                    gdouble         offset_y,
+                    GtkTextHandle  *handle)
+{
+  GtkTextHandlePrivate *priv = handle->priv;
+  gdouble start_x, start_y;
+  gint pos, x, y;
+
+  pos = _text_handle_pos_from_widget (handle,
+                                      gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
+  gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
+
+  gtk_widget_translate_coordinates (priv->windows[pos].widget, priv->parent,
+                                    start_x + offset_x - priv->windows[pos].dx,
+                                    start_y + offset_y - priv->windows[pos].dy,
+                                    &x, &y);
+  g_signal_emit (handle, signals[HANDLE_DRAGGED], 0, pos, x, y);
 }
 
 static void
-gtk_text_handle_widget_style_updated (GtkWidget     *widget,
-                                      GtkTextHandle *handle)
+handle_drag_end (GtkGestureDrag *gesture,
+                 gdouble         offset_x,
+                 gdouble         offset_y,
+                 GtkTextHandle  *handle)
 {
-  GtkTextHandlePrivate *priv;
+  GtkTextHandlePrivate *priv = handle->priv;
+  gint pos;
 
-  priv = handle->priv;
-  gtk_style_context_set_parent (gtk_widget_get_style_context (widget),
-                                gtk_widget_get_style_context (priv->parent));
+  pos = _text_handle_pos_from_widget (handle,
+                                      gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
+  g_signal_emit (handle, signals[DRAG_FINISHED], 0, pos);
+  priv->windows[pos].dragged = FALSE;
+  gtk_text_handle_unset_state (handle, pos, GTK_STATE_FLAG_ACTIVE);
+}
 
-  _gtk_text_handle_update (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
-  _gtk_text_handle_update (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
+static gboolean
+snapshot_func (GtkGizmo    *gizmo,
+               GtkSnapshot *snapshot)
+{
+  GtkCssStyle *style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (gizmo)));
+
+  gtk_css_style_snapshot_icon (style,
+                               snapshot,
+                               gtk_widget_get_width (GTK_WIDGET (gizmo)),
+                               gtk_widget_get_height (GTK_WIDGET (gizmo)),
+                               GTK_CSS_IMAGE_BUILTIN_HANDLE);
+  return TRUE;
 }
 
 static GtkWidget *
@@ -290,18 +231,21 @@ _gtk_text_handle_ensure_widget (GtkTextHandle         *handle,
     {
       GtkWidget *widget, *window;
       GtkStyleContext *context;
+      GtkEventController *controller;
 
-      widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+      widget = gtk_gizmo_new (I_("cursor-handle"),
+                              NULL, NULL, snapshot_func);
 
       gtk_widget_set_direction (widget, priv->windows[pos].dir);
 
-      g_signal_connect (widget, "draw",
-                        G_CALLBACK (gtk_text_handle_widget_draw), handle);
-      g_signal_connect (widget, "event",
-                        G_CALLBACK (gtk_text_handle_widget_event), handle);
-      g_signal_connect (widget, "style-updated",
-                        G_CALLBACK (gtk_text_handle_widget_style_updated),
-                        handle);
+      controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
+      g_signal_connect (controller, "drag-begin",
+                        G_CALLBACK (handle_drag_begin), handle);
+      g_signal_connect (controller, "drag-update",
+                        G_CALLBACK (handle_drag_update), handle);
+      g_signal_connect (controller, "drag-end",
+                        G_CALLBACK (handle_drag_end), handle);
+      gtk_widget_add_controller (widget, controller);
 
       priv->windows[pos].widget = g_object_ref_sink (widget);
       window = gtk_widget_get_ancestor (priv->parent, GTK_TYPE_WINDOW);
@@ -309,7 +253,6 @@ _gtk_text_handle_ensure_widget (GtkTextHandle         *handle,
 
       context = gtk_widget_get_style_context (widget);
       gtk_style_context_set_parent (context, gtk_widget_get_style_context (priv->parent));
-      gtk_css_node_set_name (gtk_widget_get_css_node (widget), I_("cursor-handle"));
       if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
         {
           gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
index 52461e078949b5182cfd5c688d84c5e83eea543d..f918399370b3e227e0d3a37540b74494d0e38ef4 100644 (file)
@@ -4420,6 +4420,12 @@ cursor-handle {
   background-image: none;
   box-shadow: none;
   border-style: none;
+  min-width: 20px;
+  min-height: 24px;
+  padding-left: 20px;
+  padding-right: 20px;
+  padding-top: 24px;
+  padding-bottom: 24px;
 
   @each $s,$as in ('',''),
                   (':hover','-hover'),
@@ -4428,14 +4434,12 @@ cursor-handle {
       $_url: 'assets/text-select-start#{$as}#{$asset_suffix}';
       -gtk-icon-source: -gtk-scaled(url('#{$_url}.png'),
                                     url('#{$_url}@2.png'));
-      padding-left: 10px;
     }
 
     &.bottom#{$s}:dir(ltr), &.top#{$s}:dir(rtl) {
       $_url: 'assets/text-select-end#{$as}#{$asset_suffix}';
       -gtk-icon-source: -gtk-scaled(url('#{$_url}.png'),
                                     url('#{$_url}@2.png'));
-      padding-right: 10px;
     }
 
     &.insertion-cursor#{$s}:dir(ltr), &.insertion-cursor#{$s}:dir(rtl) {